Black Friday Sale Upgrade Your Home →

Create a DynamoDB table with AWS CDK

Let's hook our app to a database. We'll be using DynamoDB, which is a NoSQL database.

Storage Illustration

🤔 Side note: What is NoSQL?

NoSQL allows you to add any kind of data in your database because it is flexible. So we don't have to design a precise schema in advance, we can just start adding our data.

Run:

  • npm install --save @aws-cdk/aws-dynamodb

Import dynamodb to our backend then create a new dynamo db table.

TS
// save it as a const since we'll use it in a little bit
const todosTable = new dynamodb.Table(this, "TodoTable", {
//a unique key
partitionKey: { name: "id", type: dynamodb.AttributeType.STRING }
});

👍 Once deployed, you can find the dynamodb resources in the aws console (Services - CloudFormattion - TodoAppStack).

Let's add an item to the table: click on the Items tab, then Create item.

Add an id then append to String items:

TS
id: String: 123
todo String: Add DynamoDB
isComplete: true

Add DynamoDB Image

Get all items from a DynamoDB table deployed with CDK using DocumentClient API

Let's create a new lambda function that is going to be responsible for reading, creating, and deleting data in our database.

🤔 A handy dynamodb cheatsheet.

👍 Create todoHandler.ts (we can also delete the other hello lambda function since we just used it for testing).

TS
import AWS = require("aws-sdk");
// the table name that we get from an env variable
const tableName = process.env.TABLE_NAME || "";
// for interacting with dynamoDB from JavaScript / nodeJS
const dynamo = new AWS.DynamoDB.DocumentClient();
const createResponse = (
body: string | AWS.DynamoDB.DocumentClient.ItemList,
statusCode = 200
) => {
return {
statusCode,
body: JSON.stringify(body, null, 2)
};
};
// DynamoDB Scan operation scans and returns all of the items in the db
const getAllTodos = async () => {
const scanResult = await dynamo
.scan({
TableName: tableName
})
.promise();
return scanResult;
};
// async function that response to apiGateway events
exports.handler = async function(event: AWSLambda.APIGatewayEvent) {
try {
const { httpMethod, body: requestBody } = event;
// GET request
if (httpMethod === "GET") {
const response = await getAllTodos();
return createResponse(response.Items || []);
}
return createResponse(
`We only accept GET requests for now, not ${httpMethod}`,
500
);
} catch (error) {
console.log(error);
return createResponse(error, 500);
}
};

We'll have to make some changes to our todo-backend file. Let's make a new lambda function:

import * as lambda from "@aws-cdk/aws-lambda";

TS
// use this instead of const, you'll see why in a moment
this.handler = new lambda.Function(this, "TodoHandler", {
code: lambda.Code.fromAsset("lambda"),
// the name of the method in your code that lambda will call
// our file is called `todoHandler.ts` and it `exports.handler`
handler: "todoHandler.handler",
runtime: lambda.Runtime.NODEJS_12_X,
// we need to pass the name of our table as env variable
environment: {
TABLE_NAME: todosTable.tableName
}
});

Debug permission issues and allow a lambda function to access data from a DynamoDB table

Let's deploy our changes and test the new lambda function.

A successful deploy will output a url (mine looks like this: https://6olvq234234.execute-api.eu-central-1.amazonaws.com/prod/).

Unfortunately, if you curl that url (or paste it into your web browser), you'll discover the following error:

  • AccessDeniedException

To debug this, let's go to the aws console and look for our lambda function (under resources in Cloudformation).

Click Monitoring and View Cloud logs, then click on the latest log stream. You should find an error like this:

  • is not authorized to perform: dynamodb:Scan on resource:

👍 By default, aws follows the principle of the least privilege, so we'll have to add some permissions.

We need this line of code to our lambda:

  • todosTable.grantReadWriteData(this.handler);

Test if the fix worked, by curl-ing the outputted url. It should return your todos.

  Previous   💼   Next